package com.virtualparadigm.compositor;

import java.io.Serializable;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import com.virtualparadigm.identity.Identifiable;

//===================================================================
// The purpose of the required aggregator/aggregate part is to 
// only allow the aggregator to survive if all of the required 
// aggregate parts exist. If one aggregate part is removed, 
// it doesnt make sense for the aggregator to exist. An example 
// of this is an xref table. If both sides of the key dont 
// exist, the record should not exist.
//
// In order to do this, if one of the children no longer exists, 
// the compositor of the aggregator should remove it. The 
// repercussion of this statement is that ALL required aggregators 
// MUST have a parent. This means we cannot use this at the top 
// level run time object, since it does not have a parent and 
// thus does not extend any CompositePart. This seems to make
// sense since the top level object really should never be 
// removed. So the top level object should never use any required 
// aggregates
//===================================================================
//public class OneToOneRequiredAggregator<ThisType extends Identifiable, ThatType extends Identifiable> implements Serializable
public class OneToOneRequiredAggregator<ThisType extends CompositePart<? extends Identifiable>, ThatType extends Identifiable> implements Serializable
{
	private static final long serialVersionUID = 1L;
	
	// Why need "wrapped" Aggregate instance?
	//   - b/c aggregate is implemented via composition
	private ThisType instance;
	private OneToOneRequiredAggregatePart<ThatType, ThisType> oneToOneRequiredAggregatePart;

	public OneToOneRequiredAggregator(ThisType instance, OneToOneRequiredAggregatePart<ThatType, ThisType> oneToOneRequiredAggregatePart) 
	{
		this.setInstance(instance);
		this.setOneToOneRequiredAggregatePart(oneToOneRequiredAggregatePart);
	}
	

	public ThisType getInstance()
	{
		return instance;
	}
	private void setInstance(ThisType instance)
	{
		this.instance = instance;
	}
	
	// package protected
	OneToOneRequiredAggregatePart<ThatType, ThisType> getOneToOneRequiredAggregatePart()
	{
		return this.oneToOneRequiredAggregatePart;
	}
	public ThatType getRequiredAggregatePart()
	{
		ThatType typedRequiredAggregatePart = null;
		OneToOneRequiredAggregatePart<ThatType, ThisType> oneToOneRequiredAggregatePart = this.getOneToOneRequiredAggregatePart();
		if(oneToOneRequiredAggregatePart != null)
		{
			typedRequiredAggregatePart = oneToOneRequiredAggregatePart.getInstance();
		}
		return typedRequiredAggregatePart;
	}
	
	// package protected
	boolean setOneToOneRequiredAggregatePart(OneToOneRequiredAggregatePart<ThatType, ThisType> oneToOneRequiredAggregatePart) 
	{
		if(this.oneToOneRequiredAggregatePart == null)
		{
			if(oneToOneRequiredAggregatePart != null)
			{
				this.oneToOneRequiredAggregatePart = oneToOneRequiredAggregatePart;
				oneToOneRequiredAggregatePart.setOneToOneRequiredAggregator(this);
			}
		}
		else
		{
			if(!this.oneToOneRequiredAggregatePart.equals(oneToOneRequiredAggregatePart))
			{
				OneToOneRequiredAggregatePart<ThatType, ThisType> tempRequiredAggregatePart = this.oneToOneRequiredAggregatePart;
				this.oneToOneRequiredAggregatePart = oneToOneRequiredAggregatePart;
				tempRequiredAggregatePart.setOneToOneRequiredAggregator(null);
				if(oneToOneRequiredAggregatePart == null)
				{
					//since im required, tell the aggregator's compositor to remove the aggregator
					this.removeFromCompositor();
				}
				else
				{
					oneToOneRequiredAggregatePart.setOneToOneRequiredAggregator(this);
				}
			}
		}
		return true;
	}
	
	boolean removeFromCompositor()
	{
		boolean status = false;
		if(this.instance != null && this.instance.getCompositor() != null)
		{
			this.instance.getCompositorDelegate().removeCompositePart(this.instance.getResourceIdentifier());
		}
		return status;
	}
	
	public boolean setRequiredAggregatePart(ThatType requiredAggregatePart)
	{
		OneToOneRequiredAggregatePart<ThatType, ThisType> oneToOneRequiredAggregatePart = null;
		if(requiredAggregatePart != null)
		{
			oneToOneRequiredAggregatePart = new OneToOneRequiredAggregatePart<ThatType, ThisType>(requiredAggregatePart, this);
		}
		return this.setOneToOneRequiredAggregatePart(oneToOneRequiredAggregatePart);
	}


	// ================================================
	// UTILITY METHODS
	// ================================================
	public String toString()
	{
//		return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
		return ReflectionToStringBuilder.toString(this, ToStringStyleBuilder.toStringStyle);
	}
	public int hashCode()
	{
		HashCodeBuilder builder = new HashCodeBuilder();
	    builder.append(this.getInstance().getResourceIdentifier());
	    return builder.toHashCode();		
	}
	public boolean equals(Object obj)
	{
		if (obj == null)
		{
			return false;
		}
		if (obj == this)
		{
			return true;
		}
		if (obj.getClass() != getClass())
		{
			return false;
		}
		OneToOneRequiredAggregator<ThisType, ThatType> that = (OneToOneRequiredAggregator<ThisType, ThatType>)obj;
		EqualsBuilder builder = new EqualsBuilder();
	    builder.append(this.getInstance().getResourceIdentifier(), that.getInstance().getResourceIdentifier());
		return builder.isEquals();
	}
	
}